#include "stdafx.h"
#include "bytecode.h"

/*
 write a symbol to string and return symbol size.
 a symbol is 2 bytes long for WIDE cases, otherwise
 it just takes 1 byte like in ascii operations.
*/
static int write_str(u8* dst, TCHAR in, int charmode, int binmode)
{
	switch(charmode)
	{
	case ENCODING_BYTE:
		*dst=(u8)in;
		return 1;
	case ENCODING_WIDE:
		if(binmode==LITTLE_ENDIAN)
		{
			dst[0]=in&0xFF;
			dst[1]=in>>8;
		}
		else
		{
			dst[1]=in&0xFF;
			dst[0]=in>>8;
		}
		return 2;
	default:
		// this is not supposed to happen
		return 1;
	}
	return 0;
}

/* returns size of a symbol */
static int get_char_len(int charmode)
{
	if(charmode==ENCODING_BYTE) return 1;
	else if(charmode==ENCODING_WIDE) return 2;

	return 0;
}

static int find_byte_pos (TCHAR* src, TCHAR c)
{
	int i=0;
	while (src[i] != c)
		i++;

	return i;
}

/*
 parse bytecode string into a u8 array. it assumes all the
 characters use the locale encoding.

 parameters:
 - dest: holds the result of the conversion
 - src: source data containing the string to be parsed
 - iLengthgthOfSource: lazy way to obtain GetLength()
 - charmode: determines the size of a symbol (BYTE=1 byte, WIDE=2 byte)
 - binmode: endianness, used to swap bytes around or not

 returns encoded string size.
*/
int bytecode_translate(u8* dest, TCHAR* src, int iLengthgthOfSource, int charmode, int binmode)
{
	int i, di=0, bclen;

	for (i=0; i<iLengthgthOfSource; i++)
	{
		if ((bclen = is_bytecode (&(src[i]), iLengthgthOfSource-i)) > 0) 
		{
			parse_bytecode (&dest[di], &src[i], iLengthgthOfSource-i, binmode);
			di += bclen;
			i += find_byte_pos (&src[i], '>');
		}
		else 
		{
			// take care of \\ and \< entries
			if (src[i] == '\\')
			{
				if (i+1 < iLengthgthOfSource)
				{
					// '\<'
					if (src[i+1] == '<')
					{
						//dest[di++] = '<'; 
						di+=write_str(&dest[di],'<',charmode,binmode);
						i++;
					}
					// '\\'
					else if( src[i+1] == '\\' )
					{
						//dest[di++] = '\\';
						di+=write_str(&dest[di],'\\',charmode,binmode);
						i++;
					}
					// regular symbol
					else di+=write_str(&dest[di],src[i],charmode,binmode);
				}
				// regular symbol
				else di+=write_str(&dest[di],src[i],charmode,binmode);
			}
			// regular symbol
			else di+=write_str(&dest[di],src[i],charmode,binmode);
		}
	}
	return di;
}

// parse a bytecode
int parse_bytecode (u8* dest, TCHAR* src, int iLengthgthOfSource, int binmode)
{
	int i, k=0;
	TCHAR cBuf[50];
	for (i=4; i<iLengthgthOfSource; i++)
	{
		if (src[i]=='>')
			break;
		else
		{
			cBuf[k++] = src[i];
		}
	}
	cBuf[k] = 0;
	int value;
	float fvalue;
	double dvalue;
	switch (src[2]) 
	{
	case 'd':
		_stscanf (cBuf, _T("%d"), &value);
		break;

	case 'h':
		_stscanf (cBuf, _T("%X"), &value);
		break;

	case 'l':
		_stscanf (cBuf, _T("%f"), &fvalue);
		break;

	case 'o':
		_stscanf (cBuf, _T("%lf"), &dvalue);
		break;
	}

	if (binmode == LITTLE_ENDIAN)
	{
		switch (src[1])
		{
		case 'b':
			dest[0] = (char) value;
			break;

		case 'w':
			dest[0] = (value & 0xff);
			dest[1] = (value & 0xff00)>>8;
			break;
		
		case 'l':
			dest[0] = (value & 0xff);
			dest[1] = (value & 0xff00)>>8;
			dest[2] = (value & 0xff0000)>>16;
			dest[3] = (value & 0xff000000)>>24;
			break;

		case 'f':
			*((float*)dest) = fvalue;
			break;

		case 'd':
			*((double*)dest) = dvalue;
			break;
		}
	}
	else 
	{
		switch (src[1])
		{
		case 'b':
			dest[0] = (u8) value;
			break;

		case 'w':
			dest[0] = HIBYTE (LOWORD (value));
			dest[1] = LOBYTE (LOWORD (value));
			break;
		
		case 'l':
			dest[0] = HIBYTE (HIWORD (value));
			dest[1] = LOBYTE (HIWORD (value));
			dest[2] = HIBYTE (LOWORD (value));
			dest[3] = LOBYTE (LOWORD (value));
			break;

		case 'f':
			{
				u8* p = (u8*) &fvalue;
				int i;
				for (i=0; i<4; i++)
				{
					dest[i] = p[3-i];
				}
			}
			break;

		case 'd':
			{
				u8* p = (u8*) &dvalue;
				int i;
				for (i=0; i<8; i++)
				{
					dest[i] = p[7-i];
				}
			}
			break;
		}
	}
	return value;
}

/* calculate size of a bytecode sequence */
int calc_bytecode_translation_destinationlength (TCHAR* src, int iLengthgthOfSource, int charmode)
{
	int i, iDestinationLength = 0, l, k;
	for (i=0; i<iLengthgthOfSource; i++)
	{
		if ((l = is_bytecode (&(src[i]), iLengthgthOfSource-i)) == 0)
		{
			if (src[i] == '\\')
			{
				if (i+1 < iLengthgthOfSource)
				{
					if (src[i+1] == '<')
					{
						iDestinationLength+=get_char_len(charmode);
						i++;
					}
					else if( src[i+1] == '\\' )
					{
						iDestinationLength+=get_char_len(charmode);
						i++;
					}
					else
					{
						iDestinationLength+=get_char_len(charmode);
					}
				}
				else
				{
					iDestinationLength+=get_char_len(charmode);
				}
			}
			else
			{
				iDestinationLength+=get_char_len(charmode);
			}
		}
		else
		{
			iDestinationLength += l;
			for (k=i; i<iLengthgthOfSource; k++)
			{
				if (src[k]=='>')
					break;
			}
			i = k;
		}
	}
	return iDestinationLength;
}

/* determine if the sequence is a bytecode */
int is_bytecode (TCHAR* src, int len)
{
	int i=0;

	if (src[i] == '<')
	{
		if (i+1 < len)
		{
			switch (src[i+1])
			{
			case 'b': case 'w': case 'l': case 'f': case 'd':
				if (i+2 < len)
				{
					switch (src[i+2])
					{
						case 'd': case 'h': case 'l': case 'o':
							if (i+3 < len)
							{
								if (src[i+3] == ':')
								{
									int j,k;
									for (j=4; j < len; j++)
									{
										if (src[i+j] == '>')
											break;
									}
									if (j==4 || j==len)
										return FALSE;
									for (k=4; k<j; k++)
									{
										switch (src[i+2])
										{
										case 'd':
											if ((src[i+k]>='0' && src[i+k]<='9') || src[i+k]=='-')
												continue;
											else
												return FALSE; 
											break;

										case 'h':
											if ((src[i+k]>='0' && src[i+k]<='9') ||
											 (src[i+k]>='a' && src[i+k]<='f'))
												continue;
											else
												return FALSE; 
											break;

										case 'o': case 'l': 
											if ((src[i+k]>='0' && src[i+k]<='9') || src[i+k]=='-' || src[i+k]=='.' || src[i+k]=='e' || src[i+k]=='E')
												continue;
											else
												return FALSE;
											break;
										}
									}
									switch (src[i+1])
									{
									default:
									case 'b': return 1;
									case 'w': return 2;
									case 'l': return 4;
									case 'f': return 4;
									case 'd': return 8;
									}
								}
								else
									return FALSE; 
							}
							else
								return FALSE; 
							break;
						
						default:
							return FALSE; 
					}
				}
				else
					return FALSE; 
				break;
			
			default:
				return FALSE; 
				break;
			}
		}
		else
			return FALSE; 
	}
	else
		return FALSE; 
}

/* translate string to binary (pre-allocates the destination) */
int bytecode_translate (u8** ppd, TCHAR* src, int iLengthgthOfSource, int charmode, int binmode)
{
	int iDestinationLength = calc_bytecode_translation_destinationlength (src, iLengthgthOfSource, charmode);
	if (iDestinationLength > 0)
	{
		*ppd = new u8[iDestinationLength];
		bytecode_translate (*ppd, src, iLengthgthOfSource, charmode, binmode);
		return iDestinationLength;
	}
	else
	{
		*ppd = NULL;
		return 0;
	}
}

int translate_bytes_to_bytecode (TCHAR* pd, unsigned char* src, int iLengthgthOfSource)
{
	int i, k = 0;
	TCHAR cBuf[16];
	for (i=0; i<iLengthgthOfSource; i++)
	{
		if (src[i] == '<')
		{
			pd[k++] = '\\';
			pd[k++] = '<';
		}
		else if( src[i] == '\\' )
		{
			pd[k++] = '\\';
			pd[k++] = '\\';
		}
		else if (src[i] >= 32 && src[i] < 127)
		{
			pd[k++] = src[i];
		}
		else if( src[i]==10 || src[i]==13 )
		{
			pd[k++] = src[i];
		}
		else
		{
			pd[k++] = '<';
			pd[k++] = 'b';
			pd[k++] = 'h';
			pd[k++] = ':';
			_stprintf (cBuf, _T("%2.2x"), src[i]);
			pd[k++] = cBuf[0];
			pd[k++] = cBuf[1];
			pd[k++] = '>';
		}
	}
	pd[k] = '\0';
	return k+1;
}

int byte_to_bytecode_destinationlength (char* src, int iLengthgthOfSource)
{
	int i, iDestinationLength = 1;
	for (i=0; i<iLengthgthOfSource; i++)
	{
		if (src[i] == '<')
			iDestinationLength+=2; 
		else if( src[i] == '\\' )
			iDestinationLength+=2; 
		else if (src[i] >= 32 && src[i] < 127)
			iDestinationLength++; 
		else if( src[i]==10 || src[i]==13 )
			iDestinationLength++; 
		else
			iDestinationLength+=7; 
	}
	return iDestinationLength;
}
